﻿using AlgoritmoConstrutivo.entidades;
using Entidades;
using RandomKeyGenerator;
using Support;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Algoritmos
{
    public abstract class  Algoritmo
    {

        public List<int> ordemCabecasDeChave = new List<int>();
        protected int NUM_CLUSTERS = 0;

        public abstract void ConfigurarEstratégiaAlocacao(string estrategia);
        public abstract Grafo Decoder(int numCromossos, Grafo software, Cromossomo cromossomo, int individuo,String estrategiaAlocacao);
        public abstract Grafo Decoder(int numCromossos, Grafo software, Cromossomo[] chaves, int individuo); //somente para manter compatibilidade
        public abstract Grafo Decoder(int numCromossos, Grafo software, Cromossomo cromossomo, int individuo, String estrategiaAlocacao, Configuration2 cfg);

        public abstract Grafo Decoder(int numCromossos, Grafo software, Cromossomo cromossomo, int individuo,  Configuration2 cfg);


        /// <summary>
        /// Inicializa uma quantidade NUM_CLUSTER colocando os NUM_CLUSTERS nós em ordem decrescente do número de vizinhos que estes possuem.
        /// </summary>
        /// <param name="software">Grafo representando um software</param>
        /// <param name="idOriginalCromossomo">??</param>
        protected void ConfiguraCabeçasDeChave(Grafo software, int idOriginalCromossomo, Cromossomo[] chaves, Configuration2 cfg)
        {
            //esta instância é configurada com sua posição dentro da população.
            software.IdOriginalCromossomo = idOriginalCromossomo;

            this.DefineQtdeClusters(chaves, idOriginalCromossomo, software);
            this.DefineCabecasChave(software,cfg);

           
        }     

        protected void ColocaModulosUnitariosMenosPiorPorCromossomo(Gene[] cromossomo, Grafo software)
        {

            Decimal mfNovo = 0;
            Decimal mfMaximo = 0;
            bool melhoreiMF = false;
            Cluster clMelhorMovimento = null;

            Cluster clusterDeUnitarios = new Cluster(true);

            for (int i = 0; i < software.RetornarNumClusters(); i++)
            {
                if (software.RetornarCluster(i) == null)
                {
                    throw new NullReferenceException("Tentou-se acessar o cluster de id "+i+ " mas ele estava null");
                }

                if (software.RetornarCluster(i).RetornarNumNosCluster() == 1)
                {
                    int IdDeClusterUnitario = software.RetornarCluster(i).RetornaIdPrimeiroNo();
                    No noClusterUnitario = software.RetornaNo(IdDeClusterUnitario);



                    for (int k = 0; k < software.RetornarNumClusters(); k++)
                    {
                        Cluster clVisitado = software.RetornarCluster(k);

                        melhoreiMF = clVisitado.AvaliaInsercaoDeUmNo(software, noClusterUnitario.Id, out mfNovo);

                        if (melhoreiMF && mfNovo > mfMaximo)
                        {
                            clMelhorMovimento = clVisitado;
                            mfMaximo = mfNovo;
                            mfNovo = 0;
                        }
                    }

                    if (mfMaximo != 0) //algum cluster foi atribuído a ele. Se por acaso nenhum cluster for atribuído a ele, ele fica no cluster que está;
                        software.ConfigurarClusterDeUmNo(clMelhorMovimento, noClusterUnitario);

                    if (noClusterUnitario.Cluster == null)
                        throw new NullReferenceException("Cluster nulllllll");
                }
            }
        }

        protected void ColocaModulosUnitariosMenosPior(Cromossomo[] chaves, Grafo software, int individuo)
        {

            Decimal mfNovo = 0;
            Decimal mfMaximo = 0;
            bool melhoreiMF = false;
            Cluster clMelhorMovimento = null;

            Cluster clusterDeUnitarios = new Cluster(true);

            for (int i = 0; i < software.RetornarNumClusters(); i++)
            {

                if (software.RetornarCluster(i).RetornarNumNosCluster() == 1)
                {
                   int IdDeClusterUnitario = software.RetornarCluster(i).RetornaIdPrimeiroNo();
                   No noClusterUnitario = software.RetornaNo(IdDeClusterUnitario);



                    for (int k = 0; k < software.RetornarNumClusters(); k++)
                    {
                        Cluster clVisitado = software.RetornarCluster(k);

                        melhoreiMF = clVisitado.AvaliaInsercaoDeUmNo(software, noClusterUnitario.Id, out mfNovo);

                        if (melhoreiMF && mfNovo > mfMaximo)
                        {
                            clMelhorMovimento = clVisitado;
                            mfMaximo = mfNovo;
                            mfNovo = 0;
                        }
                    }

                    if (mfMaximo != 0) //algum cluster foi atribuído a ele.
                        software.ConfigurarClusterDeUmNo(clMelhorMovimento, noClusterUnitario);


                    else //tenho que decidir o que fazer com esse cara.
                        software.ConfigurarClusterDeUmNo(clusterDeUnitarios, noClusterUnitario);

                }
            }
        }

        protected void AgrupaClustersUnitarios(Cromossomo[] chaves, Grafo software, int individuo)
        {
            Cluster ClUnitario = null;


            for (int i = 0; i < software.RetornarNumClusters(); i++)
            {
                if (software.RetornarCluster(i).RetornarNumNosCluster() == 1)
                {
                    if (ClUnitario == null)
                        ClUnitario = new Cluster(true);

                    software.ConfigurarClusterDeUmNo(ClUnitario, software.RetornaNo(software.RetornarCluster(i).RetornarNo(0)));

                }
            }
        }


        protected void DefineQtdeClusters(Cromossomo cromossomo, Grafo software)
        {
            Cluster.ZeraNumClusters();

            //o numero de clusters para o cromossomo/individuo está no vetor de chaves aleatórios. No orquestrador, na primeira geração do vetor de chaves aleatórias, 
            //ele foi definido.
            NUM_CLUSTERS = cromossomo.Value;

            //os clusters são criados
            software.ConfigurarClusters(NUM_CLUSTERS);
        }

        protected void DefineQtdeClusters(Cromossomo[] chaves, int idOriginalCromossomo, Grafo software)
        {
            Cluster.ZeraNumClusters();

            //o numero de clusters para o cromossomo/individuo está no vetor de chaves aleatórios. No orquestrador, na primeira geração do vetor de chaves aleatórias, 
            //ele foi definido.
            NUM_CLUSTERS = chaves[idOriginalCromossomo].Value;

            //os clusters são criados
            software.ConfigurarClusters(NUM_CLUSTERS);
        }

        protected virtual void DefineCabecasChave(Grafo software, Configuration2 cfg)
        {
            FabricaCromossomos rk = new FabricaCromossomos();

            //esta variável irá definir a quantidade de número de nós avaliada para se tornar um cabeça de chave
            int limiteBuscaModulos = (int)NUM_CLUSTERS + (int)(software.RetornarNumNos() * cfg.LimiteBuscaDosNos);

            //se por acaso esse valor passar do número existente de nós, todos os nós serão avaliados
            if (limiteBuscaModulos >= software.RetornarNumNos())
                limiteBuscaModulos--;

            //os nós serão ordenados descrescentemente pela sua quantidade de vizinhos, por exemplo, o nó com maior quantidade de vizinhos será o primeiro da lista.s
            List<No> nosLista = software.RetornaNoAsList();
            nosLista.Sort();
          

            No n = null;
            int count = 0;

            //os clusters começam a ter os nós atribuídos.
            for (int k = 0; k < NUM_CLUSTERS; k++)
            {
                count = 0;
                software.RetornarCluster(k).InicializarNos();

                do
                {
                    count++;
                  

                    if (this.ordemCabecasDeChave.Count > 0)
                    {
                        n = software.RetornaNo(this.ordemCabecasDeChave[0]);
                        this.ordemCabecasDeChave.Remove(this.ordemCabecasDeChave[0]);
                    
                    }
                    else 
                    {
                        n = nosLista[RandomNumberGenerator.GenerateRandomNumberFromInterval(0, limiteBuscaModulos)];
                        software.OrdemAtribuicaoCabecasChave.Add(n.Id);
                    }

                }
                while (n.Cluster != null && count < nosLista.Count);


                //se por acaso todos no intervalo de buscas já tiverem um cluster, nada é feito
                if (n.Cluster == null)
                {
                    try
                    {
                        software.ConfigurarClusterDeUmNo(software.RetornarCluster(k), n);
                    }
                    catch (Exception ex)
                    {
                        
                        throw ex;
                    }
                    this.ConfiguraClustersVizinhos(n.Id, software, software.RetornarCluster(k),cfg);

                }

            }
        }


      

        protected void ConfiguraClustersVizinhos(int indice, Grafo software, Cluster cl, Configuration2 cfg)
        {
            Decimal numVizinhos =(Decimal) Math.Round(software.RetornaNo(indice).ArestasQueSaem.Count * cfg.Porc_Vizinhos);


            int vizinhos = (int)numVizinhos;


            for (int i = 0; i < vizinhos; i++)
            {
                if (software.RetornaNo(indice).ArestasQueSaem[i].Cluster == null)
                    software.ConfigurarClusterDeUmNo(cl, software.RetornaNo(indice).ArestasQueSaem[i]);
            }
        }


        #region buscal local


        protected void UpdateCromossomos(int indiceNo, Cluster novo, int numClusters, Cromossomo[] numerosAleatorios, int individuo, int numModulos)
        {
            double novoNumeroAleatorio = (double)novo.Id / (double)numClusters;


            Gene novoRK = new Gene();
            Gene antigoRandomKEY = numerosAleatorios[individuo].Chaves[indiceNo];

            novoRK.Indice = antigoRandomKEY.Indice;
            novoRK.KeyOrdenacao = antigoRandomKEY.KeyOrdenacao;

            novoRK.KeyCriterio = Math.Round(novoNumeroAleatorio, 2);

            numerosAleatorios[individuo].Chaves[indiceNo] = novoRK;

            antigoRandomKEY = null;

        }

        public void BuscaLocal(Cromossomo cromossomo, Grafo software)
        {
            Array.Sort(cromossomo.Chaves);

            Cluster clMelhorMovimentoNovo = null;
            Cluster clMelhorMovimentoAntigo = null;


            Decimal mfClusterDestino = 0;
            Decimal mfClusterOrigem = 0;
            No noASerAvaliado = null;
            int idNoASerAtualizado = 0;

            Decimal GanhoMfMaximo = 0;

            // Para todos os nós do Grafo.
            for (int noCorrente = 0; noCorrente < software.RetornarNumNos(); noCorrente++)
            {
                Gene NoComClusterASerAvaliado = cromossomo.Chaves[noCorrente];
                bool melhoreiAntigoCluster = false;

                Decimal deltaClusterAntigo = 0;
                Decimal deltaClusterNovo = 0;

                noASerAvaliado = software.RetornaNo(NoComClusterASerAvaliado.Indice);
                clMelhorMovimentoNovo = new Cluster(false);
                clMelhorMovimentoAntigo = new Cluster(false);

                //vou verificar para todo cluster que não é daquele nó, qual o melhor que ele se encaixa
                for (int clusterVisitado = 0; clusterVisitado < software.RetornarNumClusters(); clusterVisitado++)
                {
                    bool melhoreiNovoCluster = false;

                    if (noASerAvaliado.Cluster.Id == clusterVisitado)
                        continue;


                    try
                    {
                        melhoreiAntigoCluster = software.RetornarCluster(noASerAvaliado.Cluster.Id).AvaliaRetiradaDeUmNo(software, noASerAvaliado.Id, out mfClusterOrigem, clusterVisitado);
                        deltaClusterAntigo = mfClusterOrigem - software.RetornarCluster(noASerAvaliado.Cluster.Id).Mf;

                        melhoreiNovoCluster = software.RetornarCluster(clusterVisitado).AvaliaInsercaoDeUmNo(software, noASerAvaliado.Id, out mfClusterDestino);
                        deltaClusterNovo = mfClusterDestino - software.RetornarCluster(clusterVisitado).Mf;
                    }
                    catch (Exception ex)
                    {
                        
                        throw ex; //TODO provavelmente o erro é que eu estou colocando um cluster novo sem atualizar o vetor de clusters existentes. Talvez no momento em que eu agrupo todos em cluster unitários. O erro nesse caso foi acessar o id 73 qdo existem clusters de 0 a 72.
                    }

                    if (!melhoreiNovoCluster && !melhoreiAntigoCluster)
                        continue;


                    if (melhoreiNovoCluster && !melhoreiAntigoCluster)
                    {
                        deltaClusterAntigo = Math.Abs(deltaClusterAntigo);

                        if (deltaClusterNovo > deltaClusterAntigo && (deltaClusterNovo - deltaClusterAntigo) > GanhoMfMaximo)
                        {
                            GanhoMfMaximo = deltaClusterNovo - deltaClusterAntigo;
                            clMelhorMovimentoNovo = software.RetornarCluster(clusterVisitado);
                          //  idNoASerAtualizado = noCorrente;
                            //clMelhorMovimentoAntigo = software.RetornaNo(idNoASerAtualizado).Cluster; //BUG!!!! NAO EH ESSE ID, TENHO Q OLHAR NAS CHAVES ALEATORIAS!!!
                            clMelhorMovimentoAntigo = noASerAvaliado.Cluster; //BUG!!!! NAO EH ESSE ID, TENHO Q OLHAR NAS CHAVES ALEATORIAS!!!


                        }
                    }

                    else if (!melhoreiNovoCluster && melhoreiAntigoCluster)
                    {
                        deltaClusterNovo = Math.Abs(deltaClusterNovo);

                        if (deltaClusterAntigo > deltaClusterNovo && (deltaClusterAntigo - deltaClusterNovo) > GanhoMfMaximo)
                        {
                            GanhoMfMaximo = deltaClusterAntigo - deltaClusterNovo;
                            clMelhorMovimentoNovo = software.RetornarCluster(clusterVisitado);
                            //idNoASerAtualizado = noCorrente;
                            //clMelhorMovimentoAntigo = software.RetornaNo(idNoASerAtualizado).Cluster; //BUG!!!! NAO EH ESSE ID, TENHO Q OLHAR NAS CHAVES ALEATORIAS!!!
                            clMelhorMovimentoAntigo = noASerAvaliado.Cluster;
                        }
                    }

                    else
                    {
                        if (deltaClusterAntigo + deltaClusterNovo > GanhoMfMaximo)
                        {
                            GanhoMfMaximo = deltaClusterAntigo + deltaClusterNovo;
                            clMelhorMovimentoNovo = software.RetornarCluster(clusterVisitado);
                           // idNoASerAtualizado = noCorrente;
                           // clMelhorMovimentoAntigo = software.RetornaNo(idNoASerAtualizado).Cluster; //BUG!!!! NAO EH ESSE ID, TENHO Q OLHAR NAS CHAVES ALEATORIAS!!!

                            clMelhorMovimentoAntigo =noASerAvaliado.Cluster; //BUG!!!! NAO EH ESSE ID, TENHO Q OLHAR NAS CHAVES ALEATORIAS!!!

                        }
                    }
                }

                if (clMelhorMovimentoNovo.Id != -1) //se o id do cluster foi -1, significa que a movimentação do nó pelos clusters não melhora em nada o MQ
                {
                    software.ConfigurarClusterDeUmNo(clMelhorMovimentoNovo, noASerAvaliado); //antes era atualização.


                    if (!software.Ponderado)
                    {
                        clMelhorMovimentoAntigo.CalculaMF(software);
                        clMelhorMovimentoNovo.CalculaMF(software);
                    }
                    else
                    {
                        clMelhorMovimentoAntigo.CalculaMFGrafoPonderado(software);
                        clMelhorMovimentoNovo.CalculaMFGrafoPonderado(software);
                    }


                   
                }
            }
        }

        #endregion
    }
}
